package entry

import (
	"errors"
	"fmt"
	db "gsc-datasets/internal/db/dao"
	pgdb "gsc-datasets/internal/db/sqlc"
	"gsc-datasets/internal/rest/constants"
	appMiddleware "gsc-datasets/internal/rest/middlewares"
	appRender "gsc-datasets/internal/rest/render"
	datasetEntityRender "gsc-datasets/internal/rest/render/entity/dataset"
	"gsc-datasets/internal/rest/routers"
	"gsc-datasets/pkg/util"
	"net/http"
	"github.com/go-chi/chi/v5"
	"github.com/go-chi/render"
)

type DatasetEntryRouter struct {
	*routers.RestRouter
}

// New initialization
func New(
	rr *routers.RestRouter,
) *DatasetEntryRouter {
	s := &DatasetEntryRouter{
		rr,
	}
	s.register()
	return s
}

func (s *DatasetEntryRouter) register() {
	r := s.ChiRouter()
	r.Use(appMiddleware.WithUser(s.RestRouter, constants.UserKey), appMiddleware.WithDataset(s.RestRouter, constants.DatasetKey))
	r.With(appMiddleware.WithPager(s.RestRouter)).Get("/", s.listDatasetEntries)
	r.Post("/", s.createDatasetEntry)
	r.Route(fmt.Sprintf("/{%s}", constants.DatasetEntryKey), func(r chi.Router) {
		r.Use(appMiddleware.WithDatasetEntry(s.RestRouter, constants.DatasetEntryKey))
		r.Get("/", s.getDatasetEntry)
		r.Put("/", s.updateDatasetEntry)
		r.Delete("/", s.deleteDatasetEntry)
	})
}

// 按parent列出数据集项
func (s *DatasetEntryRouter) listDatasetEntries(w http.ResponseWriter, r *http.Request) {
	logger := s.Logger("DatasetEntryRouter")
	logger.Info("-listDatasetEntries-")
	ctx := r.Context()
	parentVal := r.URL.Query().Get("parent")
	if parentVal == "" {
		parentVal = "/"
	}
	if !util.EndWith(parentVal, "/") {
		parentVal = parentVal + "/"
	}
	datasetPtr := ctx.Value(constants.DatasetKey).(*pgdb.Dataset)
	pagerPtr := ctx.Value(util.PagerKey{}).(*util.Pager)

	logger.Infof("dataset_id:%d,parent:%s", datasetPtr.ID, parentVal)
	logger.Infof("pager:%+v", pagerPtr)

	total, err := s.Dao().CountDatasetEntriesByParent(ctx, pgdb.CountDatasetEntriesByParentParams{
		Status:    1,
		DatasetID: datasetPtr.ID,
		Parent:    parentVal,
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}

	datasetEntries, err := s.Dao().ListDatasetEntriesByParent(ctx, pgdb.ListDatasetEntriesByParentParams{
		Status:    1,
		DatasetID: datasetPtr.ID,
		Parent:    parentVal,
		Offset:    pagerPtr.Offset,
		Limit:     pagerPtr.Limit,
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	logger.Infof("datasetEntries:%d", len(datasetEntries))
	if err := render.Render(w, r, appRender.EntityPagedListToRender(total, datasetEntries)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

// 创建数据集项
func (s *DatasetEntryRouter) createDatasetEntry(w http.ResponseWriter, r *http.Request) {
	logger := s.Logger("DatasetEntryRouter")
	logger.Info("-createDatasetEntry-")
	data := &datasetEntityRender.CreateDatasetEntryRequest{}
	if err := render.Bind(r, data); err != nil {
		render.Render(w, r, appRender.ErrBadRequest(err))
		return
	}
	ctx := r.Context()
	datasetPtr := ctx.Value(constants.DatasetKey).(*pgdb.Dataset)
	userPtr := ctx.Value(constants.UserKey).(*pgdb.User)
	data.CollaborateUserID = userPtr.ID
	if !data.Suffix.Valid && util.StrContains(data.Name, ".") {
		data.Suffix = db.JNSFromString(util.StrLastPart(data.Name, "."))
	}
	if !util.EndWith(data.Dir, "/") {
		data.Dir = data.Dir + "/"
	}
	if data.Dir != "/" {
		parentDatasetEntry , err := s.Dao().GetDatasetEntryByDirAndName(ctx, pgdb.GetDatasetEntryByDirAndNameParams{
			DatasetID: datasetPtr.ID,
			Dir:  data.Dir,
		})
		if err != nil {
			render.Render(w, r, appRender.ErrRender(err))
			return
		}
		data.Level = parentDatasetEntry.Level + 1
	} else {
		data.Level = 1
	}
	var datasetEntryPtr *pgdb.DatasetEntry
	err := s.TxDao().Exec(ctx, func(q *pgdb.Queries) (err error) {
		data.UpsertDatasetEntryParams.DatasetID = datasetPtr.ID
		datasetEntry, err := q.UpsertDatasetEntry(ctx, *data.UpsertDatasetEntryParams)
		if err != nil {
			return
		}
		datasetEntryPtr = &datasetEntry
		return
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	if err := render.Render(w, r, appRender.EntityToRender(datasetEntryPtr)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

// 获取数据项
func (s *DatasetEntryRouter) getDatasetEntry(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	datasetEntryPtr := ctx.Value(constants.DatasetEntryKey).(*pgdb.DatasetEntry)
	if err := render.Render(w, r, appRender.EntityToRender(datasetEntryPtr)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

// 修改数据项标签
func (s *DatasetEntryRouter) updateDatasetEntry(w http.ResponseWriter, r *http.Request) {
	logger := s.Logger("DatasetEntryRouter")
	logger.Info("-updateDatasetEntry-")
	data := &datasetEntityRender.UpdateDatasetEntryRequest{}
	if err := render.Bind(r, data); err != nil {
		render.Render(w, r, appRender.ErrBadRequest(err))
		return
	}
	ctx := r.Context()
	datasetEntryPtr := ctx.Value(constants.DatasetEntryKey).(*pgdb.DatasetEntry)
	data.UpdateDatasetEntryParams.ID = datasetEntryPtr.ID
	if data.Level == 0 {
		data.Level = datasetEntryPtr.Level
	}
	if data.Dir == "" {
		data.Dir = datasetEntryPtr.Dir
	}
	if data.Name == "" {
		data.Name = datasetEntryPtr.Name
	}
	if data.Size == 0 {
		data.Size = datasetEntryPtr.Size
	}
	logger.Infof("名称有改变需要同步更改suffix:%s,%s,%+v", data.Name, datasetEntryPtr.Name, data.Suffix)
	if data.Name != datasetEntryPtr.Name {
		// 名称有改变需要同步更改suffix
		if util.StrContains(data.Name, ".") {
			data.Suffix = db.JNSFromString(util.StrLastPart(data.Name, "."))
		} else{
			data.Suffix = db.JNSFromString("")
		}
	}
	err := s.TxDao().Exec(ctx, func(q *pgdb.Queries) (err error) {
		datasetEntry, err := q.UpdateDatasetEntry(ctx, *data.UpdateDatasetEntryParams)
		if err != nil {
			return
		}
		datasetEntryPtr = &datasetEntry
		return
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	if err := render.Render(w, r, appRender.EntityToRender(datasetEntryPtr)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

// 删除数据项
func (s *DatasetEntryRouter) deleteDatasetEntry(w http.ResponseWriter, r *http.Request) {
	logger := s.Logger("DatasetEntryRouter")
	logger.Info("-deleteDatasetEntry-")
	ctx := r.Context()
	datasetPtr := ctx.Value(constants.DatasetKey).(*pgdb.Dataset)
	datasetEntryPtr := ctx.Value(constants.DatasetEntryKey).(*pgdb.DatasetEntry)
	if datasetEntryPtr.DatasetID != datasetPtr.ID {
		render.Render(w, r, appRender.ErrRender(errors.New(("无权删除该节点"))))
		return
	}
	err := s.TxDao().Exec(ctx, func(q *pgdb.Queries) (err error) {
		deletable, err := q.CheckDatasetEntryDeletable(ctx, datasetEntryPtr.ID)
		if err != nil {
			return
		}
		logger.Infof("-deleteDatasetEntry-deletable:%+v", deletable)
		if !deletable {
			err = fmt.Errorf("delete is forbid for fk constraint")
			return
		}
		deletedDatasetEntry, err := q.DeleteDatasetEntry(ctx, datasetEntryPtr.ID)
		if err != nil {
			return
		}
		datasetEntryPtr = &deletedDatasetEntry
		return
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	if err := render.Render(w, r, appRender.EntityToRender(datasetEntryPtr)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}
